package com.jingdianjichi.subject.infra.basic.service.impl;

import com.jingdianjichi.subject.common.entity.PageResult;
import com.jingdianjichi.subject.common.enums.SubjectInfoTypeEnum;
import com.jingdianjichi.subject.infra.basic.entity.EsSubjectFields;
import com.jingdianjichi.subject.infra.basic.entity.SubjectInfoEs;
import com.jingdianjichi.subject.infra.basic.es.EsIndexInfo;
import com.jingdianjichi.subject.infra.basic.es.EsRestClient;
import com.jingdianjichi.subject.infra.basic.es.EsSearchRequest;
import com.jingdianjichi.subject.infra.basic.es.EsSourceData;
import com.jingdianjichi.subject.infra.basic.service.SubjectEsService;
import org.apache.commons.collections4.MapUtils;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.common.text.Text;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.MatchQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightField;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.*;

@Service
public class SubjectEsServiceImpl implements SubjectEsService {
    @Override
    public boolean insert(SubjectInfoEs subjectInfoEs) {
        EsSourceData esSourceData = new EsSourceData();
        Map<String,Object> data=convertToEsSourceData(subjectInfoEs);
        esSourceData.setDocId(subjectInfoEs.getDocId().toString());
        esSourceData.setData(data);
        return EsRestClient.insertDoc(getEsIndexInfo(),esSourceData);
    }

    private Map<String, Object> convertToEsSourceData(SubjectInfoEs subjectInfoEs) {
        Map<String, Object> data = new HashMap<>();
        data.put(EsSubjectFields.SUBJECT_ID, subjectInfoEs.getSubjectId());
        data.put(EsSubjectFields.DOC_ID, subjectInfoEs.getDocId());
        data.put(EsSubjectFields.SUBJECT_NAME, subjectInfoEs.getSubjectName());
        data.put(EsSubjectFields.SUBJECT_ANSWER, subjectInfoEs.getSubjectAnswer());
        data.put(EsSubjectFields.SUBJECT_TYPE, subjectInfoEs.getSubjectType());
        data.put(EsSubjectFields.CREATE_USER, subjectInfoEs.getCreateUser());
        data.put(EsSubjectFields.CREATE_TIME, subjectInfoEs.getCreateTime());
        return data;
    }

    @Override
    public PageResult<SubjectInfoEs> querySubjectList(SubjectInfoEs subjectInfoEs) {
        PageResult<SubjectInfoEs> pageResult = new PageResult<>();
        EsSearchRequest esSearchRequest = createSearchListQuery(subjectInfoEs);

        SearchResponse searchResponse = EsRestClient.searchWithTermQuery(getEsIndexInfo(), esSearchRequest);

        List<SubjectInfoEs> subjectInfoEsList = new LinkedList<>();
        SearchHits searchHits = searchResponse.getHits();
        if(searchHits==null || searchHits.getHits()==null){
            pageResult.setPageNo(subjectInfoEs.getPageNo());
            pageResult.setPageSize(subjectInfoEs.getPageSize());
            pageResult.setRecords(subjectInfoEsList);
            pageResult.setTotal(0);
            return pageResult;
        }

        SearchHit[] hits = searchHits.getHits();
        for (SearchHit hit : hits) {
            SubjectInfoEs result = convert(hit);
            if(Objects.nonNull(result)){
                subjectInfoEsList.add(result);
            }
        }
        pageResult.setPageNo(subjectInfoEs.getPageNo());
        pageResult.setPageSize(subjectInfoEs.getPageSize());
        pageResult.setRecords(subjectInfoEsList);
        pageResult.setTotal((int) searchHits.getTotalHits().value);
        return pageResult;
    }

    private SubjectInfoEs convert(SearchHit hit) {
        Map<String, Object> sourceAsMap = hit.getSourceAsMap();
        if(CollectionUtils.isEmpty(sourceAsMap)){
            return null;
        }
        SubjectInfoEs subjectInfoEs = new SubjectInfoEs();
        subjectInfoEs.setSubjectId(MapUtils.getLong(sourceAsMap,EsSubjectFields.SUBJECT_ID));
        subjectInfoEs.setSubjectName(MapUtils.getString(sourceAsMap,EsSubjectFields.SUBJECT_NAME));
        subjectInfoEs.setSubjectAnswer(MapUtils.getString(sourceAsMap,EsSubjectFields.SUBJECT_ANSWER));
        subjectInfoEs.setDocId(MapUtils.getLong(sourceAsMap,EsSubjectFields.DOC_ID));
        subjectInfoEs.setSubjectType(MapUtils.getInteger(sourceAsMap,EsSubjectFields.SUBJECT_TYPE));
        subjectInfoEs.setScore(new BigDecimal(String.valueOf(hit.getScore())).multiply(new BigDecimal("100.00")
                                .setScale(2, RoundingMode.HALF_UP)));

        //处理name的高亮
        Map<String, HighlightField> highlightFields = hit.getHighlightFields();
        HighlightField subjectNameHighlight = highlightFields.get(EsSubjectFields.SUBJECT_NAME);
        if(Objects.nonNull(subjectNameHighlight)){
            Text[] fragments = subjectNameHighlight.getFragments();
            StringBuilder nameBuilder = new StringBuilder();
            for (Text fragment : fragments) {
                nameBuilder.append(fragment);
            }
            subjectInfoEs.setSubjectName(nameBuilder.toString());
        }
        //处理answer的高亮
        HighlightField subjectAnswerHighlight = highlightFields.get(EsSubjectFields.SUBJECT_ANSWER);
        if(Objects.nonNull(subjectAnswerHighlight)){
            Text[] fragments = subjectAnswerHighlight.getFragments();
            StringBuilder nameBuilder = new StringBuilder();
            for (Text fragment : fragments) {
                nameBuilder.append(fragment);
            }
            subjectInfoEs.setSubjectAnswer(nameBuilder.toString());
        }

        return subjectInfoEs;
    }

    //构建查询条件
    private EsSearchRequest createSearchListQuery(SubjectInfoEs subjectInfoEs) {
        EsSearchRequest esSearchRequest =new EsSearchRequest();
        BoolQueryBuilder boolQueryBuilder = new BoolQueryBuilder();
        //查询题目名称中带有的关键词
        MatchQueryBuilder nameQueryBuilder = QueryBuilders.matchQuery(EsSubjectFields.SUBJECT_NAME, subjectInfoEs.getKeyWord());
        boolQueryBuilder.should(nameQueryBuilder);
        //设置题目名称查询靠前，权重为2
        nameQueryBuilder.boost(2);
        //查询答案中带有的关键词
        MatchQueryBuilder answerQueryBuilder = QueryBuilders.matchQuery(EsSubjectFields.SUBJECT_ANSWER, subjectInfoEs.getKeyWord());
        boolQueryBuilder.should(answerQueryBuilder);
        //构建查询类型为简答题
        MatchQueryBuilder typeQueryBuilder = QueryBuilders.matchQuery(EsSubjectFields.SUBJECT_TYPE, SubjectInfoTypeEnum.BRIEF.getCode());
        //设置查询类型为简答题
        boolQueryBuilder.must(typeQueryBuilder);
        //至少命中名称或答案中的关键字
        boolQueryBuilder.minimumShouldMatch(1);

        //设置高亮
        HighlightBuilder highlightBuilder = new HighlightBuilder().field("*").requireFieldMatch(false);
        highlightBuilder.preTags("<span style = \"color:red\">");
        highlightBuilder.postTags("</span>");

        esSearchRequest.setBq(boolQueryBuilder);
        esSearchRequest.setHighlightBuilder(highlightBuilder);
        esSearchRequest.setFields(EsSubjectFields.FIELD_QUERY);
        esSearchRequest.setFrom((subjectInfoEs.getPageNo()-1)*subjectInfoEs.getPageSize());
        esSearchRequest.setSize(subjectInfoEs.getPageSize());
        esSearchRequest.setNeedScroll(false);

        return esSearchRequest;
    }

    private EsIndexInfo getEsIndexInfo() {
        EsIndexInfo esIndexInfo = new EsIndexInfo();
        esIndexInfo.setClusterName("595957f9eaf7");
        esIndexInfo.setIndexName("subject_index");
        return esIndexInfo;
    }
}
